﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Xml;
using System.Xml.Serialization;
using System.IO;
using ConfOrm;
using ConfOrm.NH;
using NHibernate.Cfg.MappingSchema;
using NHibernate.Tool.hbm2ddl;
using NHibernate.Mapping.ByCode;
using BlackMofo.DAL.NHUtils;

namespace MappingFactory.NHUtils
{
    /// <summary>
    /// 数据库表构造类
    /// </summary>
    public class NHBuilder
    {
        #region 成员变量
        /// <summary>
        /// 输出映射文件路径
        /// </summary>
        private NHParams m_MappingParams;
        /// <summary>
        /// ConfOrm组件对象
        /// </summary>
        Assembly m_Assembly = null;
        /// <summary>
        /// Orm对象
        /// </summary>
        ObjectRelationalMapper m_Orm = null;

        Mapper m_Mapper = null;
        /// <summary>
        /// 映射对象字典
        /// </summary>
        private Dictionary<HbmMapping, string> m_MappingDict = null;
        #endregion

        #region 方法
        public Mapper GetMapper()
        {
            return m_Mapper;
        }


        /// <summary>
        /// 有参构造
        /// </summary>
        /// <param name="mappingParams"></param>
        public NHBuilder(NHParams mappingParams)
        {
            m_MappingParams = mappingParams;
            m_Assembly = Assembly.Load("ConfOrm");
            m_Orm = m_Assembly.CreateInstance("ConfOrm.ObjectRelationalMapper") as ObjectRelationalMapper;
            m_Mapper = new Mapper(m_Orm);
            m_MappingDict = new Dictionary<HbmMapping, string>();
        }
        /// <summary>
        /// 构建影射文件
        /// </summary>
        public void BuildingMappingFile()
        {
            try
            {
                foreach (var item in m_MappingDict)
                {
                    var doc = Serialize(item.Key);
                    File.WriteAllText(item.Value, doc);
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// 获到文件名
        /// </summary>
        /// <param name="mapping"></param>
        /// <returns></returns>
        private string GetMappingXmlFileName(HbmMapping mapping)
        {
            string filename = (mapping.RootClasses.Length > 0 ?
                 mapping.RootClasses[0].Name :
                 DateTime.Now.Millisecond.ToString())
                 + m_MappingParams.Suffix;
            return filename;
        }

        /// <summary>
        /// 构建库表结构
        /// </summary>
        public void BuildingDBSchema(bool isDropOldTable)
        {
            try
            {
                var cfg = NHHelper.ConfigureNHibernate();
                SchemaExport se = new SchemaExport(cfg);
                //if (isDropOldTable)
                //    se.Drop(true, true);
                se.Execute(true, true, false);
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// 根据映射对象生成xml文件全名
        /// </summary>
        /// <param name="mapping"></param>
        /// <returns></returns>
        private string GetOutXmlFullFilename(HbmMapping mapping)
        {
            string filename = GetMappingXmlFileName(mapping);
            filename = m_MappingParams.OutMappingFilePath + filename;
            return filename;
        }

        /// <summary>
        /// 序列化配置映射对象到xml文件
        /// </summary>
        /// <param name="mapping"></param>
        /// <returns></returns>
        public string Serialize(HbmMapping mapping)
        {
            var setting = new XmlWriterSettings { Indent = true };
            var serializer = new XmlSerializer(typeof(HbmMapping));
            using (var memStream = new MemoryStream(2048))
            {
                using (var xmlWriter = XmlWriter.Create(memStream, setting))
                {
                    serializer.Serialize(xmlWriter, mapping);
                    memStream.Flush();
                    memStream.Position = 0;
                    var sr = new StreamReader(memStream);
                    return sr.ReadToEnd();
                }
            }
        }

        /// <summary>
        /// 添加模型映射到字典
        /// </summary>
        /// <param name="mapper"></param>
        public void AddModelMapper(ModelMapper mapper)
        {
            HbmMapping mapping = mapper.CompileMappingForAllExplicitlyAddedEntities();
            m_MappingDict.Add(mapping, GetOutXmlFullFilename(mapping));
        }
        /// <summary>
        /// 从程序集中加载映射 
        /// </summary>
        /// <param name="assemblyAddress">程序地址</param>
        public void AddMappingsFromAssembly(Assembly assembly)
        {
            Type[] types = assembly.GetExportedTypes();
            types = types.Where(x => x.Name.EndsWith("Mapper")).ToArray();

            foreach (var type in types)
            {
                if (type.Name != "BaseMapper`1")
                {
                    ModelMapper mm = new ModelMapper();
                    mm.AddMapping(type);
                    this.AddModelMapper(mm);
                }
            }
        }

        /// <summary>
        /// 从程序集中加载映射 
        /// </summary>
        /// <param name="assemblyAddress">程序地址</param>
        public void AddMappingsFromAssembly(Assembly assembly, List<MapperItem> MapperItems)
        {
            Type[] types = assembly.GetExportedTypes();
            types = types.Where(x =>
            {
                if (x.Name.EndsWith("Mapper"))
                {
                    foreach (MapperItem mi in MapperItems)
                    {
                        if (string.Equals(mi.Name, x.Name))
                        { return true; }
                    }
                }
                return false;
            }).ToArray();

            foreach (var type in types)
            {
                if (type.Name != "BaseMapper`1")
                {
                    ModelMapper mm = new ModelMapper();
                    mm.AddMapping(type);
                    this.AddModelMapper(mm);
                }
            }
        }
        #endregion
    }
}
